/*
 * Date         Author      Note
 * 2015.06.26   wzwz        dsp2812升级bug
 * 2015.06.26   wzwz        增加STM32升级
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mh/common.h>
#include <mh/dsp2812.h>
#include <mh/sram.h>
#include <mh/mac.h>
#include "knit.h"
#include <mh/mh_file.h>
#include <mh/probe.h>
#include <sys/stat.h>
#include "command.h"

/*
 * buf - 数据缓冲区
 * size - 数据大小
 * timeout - (双口RAM： 读之前的等待时间) (网络： 读等待超时值)
 */
static int update_read(MH_KNIT *p, uchar *buf, int size, int timeout)
{
    int ret = 0;
    if(p->communication == COMM_SRAM)
    {
        usleep(timeout);
        ret = RecvData(p->pmac, buf, size);
    }
    else
    {
#if 0
        struct timeval tv;
        tv.tv_sec = timeout/1000000;
        tv.tv_usec = timeout%1000000;
        ret = inet_recv_data_timeout(buf, size, tv);
#endif
    }
    return ret;
}

/*
 * buf - 数据缓冲区
 * size - 数据大小
 * wait - 发送数据成功后的等待时间
 */
static int update_write(MH_KNIT *p, uchar *buf, int size, int wait)
{
    int ret = 0;

    if(p->communication == COMM_SRAM)
    {
        ret = SendData(p->pmac, buf, size);
    }
    if(ret == 0 && wait != 0)
    {
        usleep(wait);
    }
    return ret;
}
/*
 *
 */
ushort CheckSum(unsigned short *data, unsigned short size)
{
    long sum = 0;

    while (size)
    {
        sum += *data++;
        size -= sizeof(unsigned short);
    }

    while (sum >> 16)
        sum = (sum >> 16) + (sum & 0xFFFF);

    sum = ~sum & 0xFFFF;
    if (!sum)
        sum = 0xFFFF;

    return (unsigned short)sum;
}
/*
 *
 */
void UpdateRack(MH_KNIT *p, const char *filename, char *error, char *progress)
{
    char *buf_bin;
    unsigned short *cmd_buf, *ack_buf;
    int i;
    int ret;

    if ((buf_bin = malloc(128*1024)) == 0)
    {
        *error = -1;
        return;
    }

    int usage = USAGE_F2812_NET;

    if(p->communication == COMM_SRAM)
    {
        usage = USAGE_F2812;
    }

    *progress = 0;
    /* 解密数据 */
    ret = ReadUpdateFile(filename, buf_bin, usage);
    if(ret)
    {
        printf("read update file %s: %d\n", filename, ret);
        free(buf_bin);
        *error = -2;
        return;
    }
    /* 复位2812 */
    if(p->communication == COMM_SRAM)
    {
        if(SramUpdateRack(p->pmac))
        {
            free(buf_bin);
            *error = -100;
            return;
        }
    }

    *progress = 1;
    cmd_buf = (unsigned short*)malloc(1888+128);
    ack_buf = (unsigned short*)((char*)cmd_buf + 1888);
    if (!cmd_buf)
    {
        free(buf_bin);
        *error = -3;
        return;
    }

    /**** 合法性检查 ****/
    struct dsp_program *program;

    program = (struct dsp_program*)buf_bin;
    /* 至少需要一个程序段和一个入口段 */
    if(program->section_cur < 1)
    {
        *error = -4;
        goto _exit;
    }

    for(i = 0; i <= program->section_cur; i++)
    {
        /* 检查程序的加载空间是否占用bootloader的加载空间 */
        if(program->sec[i].start < 0x3DC000 || ((program->sec[i].start
                >= 0x3F4000) && (program->sec[i].start < 0x3F7F80)))
        {
            printf("program out of load region\n");
            ret = -5;
            goto _exit;
        }
        /* 处理入口段 */
        if(program->sec[i].start == 0x3F7F80)
        {
            /* 忽略CSM_RSVD区域 */
            program->sec[i].start = 0x3F7FF6;
            program->sec[i].data += 0x76;
            program->sec[i].size = 4;
            printf("CSM_RSVD is ignored\n");
        }
    }
    if (program->entry_sec == -1)
    {
        *error = -6;
        goto _exit;
    }
    else
    {
        /* 除去bootloader区F2802可用空间为2*0x2000*2字节 */
        if (program->ptsize > 6*0x4000*2+4)
        {
            printf("program too large: %d\n", program->ptsize);
            *error = -7;
            goto _exit;
        }
    }
    /******************************************************/
    int retry, resend;
    struct cmd_hdr *cmd;
    struct ack_hdr *ack;

    cmd = (struct cmd_hdr*) cmd_buf;
    ack = (struct ack_hdr*) ack_buf;

    cmd->type = PT_CMD;
    cmd->id = 0;
    cmd->retry = 0;
    cmd->tsize = sizeof(struct cmd_hdr);
    cmd->usrdata = 0;

    /* 清空接收区中可能存在的数据,等待2812复位完成 */
    update_read(p, (uchar*)ack_buf, 128, 100000);

    /* 让下位机进入升级模式 */
    cmd->cmd = CMD_UPDATE;
    retry = 5;
    while (retry --)
    {
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 100000) != 0)
        {
            printf("send fail and do retry...\n");
            /* 先眯会儿 */
            sleep(1);
            continue;
        }
        memset((char*)ack_buf, 0, 128);

        if (update_read(p, (uchar*)ack_buf, 128, 2000000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if ((ack->id == cmd->id) && (ack->status == ACK_EOK))
                {
                    if (ack->id > 0)
                    {
                        break;
                    }
                    cmd->id ++;
                }
            }
        }
        else
        {
            printf("no input and do retry...\n");
        }
    }
    if (retry == -1)
    {
        printf("can not goto update mode\n");
        *error = -8;
        goto _exit;
    }

    *progress = 2;

    /* 擦除扇区 */
    struct erase_request *erase;
    erase = (struct erase_request*)cmd;
    cmd->tsize = sizeof(struct erase_request);
    cmd->cmd = CMD_ERASE;

    for (i = 0; i < 6; i ++)
    {
        cmd->id ++;
        cmd->retry = 0;
        erase->sectors = SECTORC << i;

        resend = 2;
Resend_E:
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if(update_write(p, (uchar*)cmd_buf, cmd->tsize, 100000) != 0)
        {
            *error = -9;
            goto _exit;
        }
        memset((char*)ack_buf, 0, 128);

        retry = 20;
ReWait_E:
        if (update_read(p, (uchar*)ack_buf, 128, 300000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if (ack->id != cmd->id || ack->status != ACK_EOK)
                {
                    if (resend--)
                    {
                        cmd->retry = 1;
                        goto Resend_E;
                    }
                    *error = -10;
                    goto _exit;
                }

                *progress += 4;
            }
            else
            {
                *error = -11;
                goto _exit;
            }
        }
        else
        {
            if (retry--)
            {
                goto ReWait_E;
            }
            *error = -12;
            goto _exit;
        }
    }

    /* 烧写程序 */

    struct program_request *p_req;
    char *dst;
    unsigned int j;
    unsigned block_size = 512;
    float f = *progress;

    p_req = (struct program_request*)cmd_buf;

    for(i = 0; i <= program->section_cur; i++)
    {
        /* 暂不处理入口段 */
        if (i == program->entry_sec)
        {
            continue;
        }

        for (j = 0; j < program->sec[i].size; j += block_size)
        {
            cmd->id ++;
            cmd->cmd = CMD_PROGRAM;
            cmd->retry = 0;

            p_req->immediately = 1; /* 下位机收到数据后立即用携带的数据烧写 */
            p_req->p_addrL = (0xFFFF) & (program->sec[i].start + j/2);
            p_req->p_addrH = (program->sec[i].start + j/2) >> 16;
            p_req->s_addrH = 0;
            p_req->s_addrL = 0;
            if ((program->sec[i].size - j) < block_size)
            {
                p_req->size = program->sec[i].size - j;
            }
            else
            {
                p_req->size = block_size;
            }
            cmd->tsize = sizeof(struct program_request) + p_req->size;

            dst = (char*)cmd_buf + sizeof(struct program_request);
            memcpy(dst, (const char*)&program->sec[i].data[j/2], p_req->size);

            resend = 2;
Resend_P:
            retry = 15;
            cmd->chksum = 0;
            cmd->chksum = CheckSum((unsigned short*)cmd, cmd->tsize);

            if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 10000) != 0)
            {
                *error = -13;
                goto _exit;
            }
            memset((char*)ack_buf, 0, 128);

Rewait_P:
            if (update_read(p, (uchar*)ack_buf, 128, 100000) == 0)
            {
                if (ack->type == PT_ACK)
                {
                    if (ack->status != ACK_EOK || ack->id != cmd->id)
                    {
                        if (resend)
                        {
                            cmd->retry = 1;
                            resend --;
                            goto Resend_P;
                        }
                        *error = -14;
                        goto _exit;
                    }
                }
                else
                {
                    if (resend)
                    {
                        cmd->retry = 1;
                        resend --;
                        goto Resend_P;
                    }
                    *error = -15;
                    goto _exit;
                }
            }
            else
            {
                if (retry)
                {
                    retry --;
                    goto Rewait_P;
                }
                *error = -16;
                goto _exit;
            }

            f += (70.0 /(program->ptsize/block_size));
            *progress = f;
            if(*progress > 99)
            {
                *progress = 99;
            }
        }
    }

    /* 设置入口点 */
    struct entry_request *ereq;
    ereq = (struct entry_request *)cmd_buf;
    cmd->chksum = 0;
    cmd->id ++;
    cmd->tsize = sizeof(struct entry_request);
    cmd->retry = 0;
    cmd->cmd   = CMD_ENTRY;

    ereq->run  = 1; /* 烧写后运行到此入口点 */
    ereq->entry[0] = program->sec[program->entry_sec].data[0];
    ereq->entry[1] = program->sec[program->entry_sec].data[1];
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
    {
        *error = -17;
        goto _exit;
    }

    retry = 8;
    memset((char*)ack_buf, 0, 128);

Rewait_ET:
    if (update_read(p, (uchar*)ack_buf, 128, 20000) == 0)
    {
        if (ack->type == PT_ACK)
        {
            if (ack->status != ACK_EOK || ack->id != cmd->id)
            {
                if (retry)
                {
                    retry --;
                    goto Rewait_ET;
                }
                *error = -18;
                goto _exit;
            }
        }
        else
        {
            *error = -19;
            goto _exit;
        }
    }
    else
    {
        if (retry)
        {
            retry --;
            goto Rewait_ET;
        }
        if (ereq->run == 0)
        {
            *error = -20;
            goto _exit;
        }
    }

    *progress = 100;

_exit:

    free(buf_bin);
    free(cmd_buf);
}
/*
 *
 */
#define FLASH_ENTRY_ADDR    0x08008000
void UpdateStm32Arm(MH_KNIT *p, const char* filename, char *perror, char* pprogress, const int usage)
{
    int ret = 0;
    char *buf_bin;
    unsigned short *cmd_buf, *ack_buf;
    long i;
    int f_size;
    FILE *f_arm;

    if ((buf_bin = malloc(128*1024)) == 0)
    {
        printf("alloc mem fail\n");
        *perror = -1;
        return;
    }
    f_arm = fopen(filename, "rb");
    if(!f_arm)
    {
        printf("open file failed...\n");
        free(buf_bin);
        *perror = -101;
        return;
    }
    fseek(f_arm, 0, SEEK_END);
    f_size = ftell(f_arm);
    fseek(f_arm, 0, SEEK_SET);
    fclose(f_arm);
    /* 解密数据 */
    ret = ReadArmUpdateFile(filename, buf_bin, usage);
    if(ret)
    {
        printf("read update file %s: %d\n", filename, ret);
        free(buf_bin);
        *perror = -2;
        return;
    }

    /* 复位2812 */
    if(p->communication == COMM_SRAM)
    {
        if(SramUpdateRack(p->pmac))
        {
            free(buf_bin);
            *perror = -100;
            return;
        }
    }

    cmd_buf = (unsigned short*)malloc(1888+128);
    ack_buf = (unsigned short*)((char*)cmd_buf + 1888);
    if (!cmd_buf)
    {
        printf("alloc buffer fail\n");
        free(buf_bin);
        *perror = -3;
        return;
    }

    struct dsp_program *program;

    program = (struct dsp_program*)buf_bin;

    /******************************************************/
    int retry, resend;
    struct cmd_hdr *cmd;
    struct ack_hdr *ack;
    struct reboot_request *reboot;//重启设备

    cmd = (struct cmd_hdr*)cmd_buf;
    ack = (struct ack_hdr*)ack_buf;

    cmd->id = 0;
    retry = 10;
Reboot:
    cmd->usrdata = 0;
    cmd->type = PT_CMD;
    cmd->retry = 0;
    cmd->cmd = CMD_REBOOT;
    cmd->tsize = sizeof(struct reboot_request);
    reboot = (struct reboot_request*)cmd;
// 这里的usage是有encript那个工程里面生成的后缀名为mh的文件决定的。纱嘴电机的是8  双系统是9
    printf("usage = %d\n",usage);
    if(usage == USAGE_STM32_DSCRG)
    {
        reboot->device = 0x81;
    }
    else
    {
        reboot->device = 0x41;
    }
    reboot->reason = 0x2013;
    cmd->chksum = 0;
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    /* 清空接收区中可能存在的数据 */
    update_read(p, (uchar*)ack_buf, 128, 500000);
    update_write(p, (uchar*)cmd_buf, cmd->tsize, 1000000);

    /* 让下位机进入升级模式 */
    printf("update mode...\n");
    cmd->cmd = CMD_UPDATE;
    cmd->usrdata = FLAG_DFORWORD | 1;
    cmd->tsize = sizeof(struct cmd_hdr);//cmd_hdr为确认包包头格式

    while (retry --)
    {
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
        {
            printf("send fail and do retry...\n");
            /* 先眯会儿 */
            sleep(3);
            continue;
        }
        memset((char*)ack_buf, 0, 128);

        if(update_read(p, (uchar*)ack_buf, 128, 3000000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if ((ack->id == cmd->id) && (ack->status == ACK_EOK))
                {
                    if (ack->id > 0)
                    {
                        break;
                    }
                    cmd->id ++;
                }
            }
        }
        else
        {
            printf("no input and do retry...\n");
            if (retry)
            {
                goto Reboot;
            }
        }
    }
    if (retry == -1)
    {
        printf("can not goto update mode\n");
        *perror = -8;
        goto _exit;
    }

    *pprogress = 2;

    //printf("erase pages...\n");
    /* 擦除扇区 */

    struct erase_request *erase;

    erase = (struct erase_request*)cmd;
    cmd->tsize = sizeof(struct erase_request);
    cmd->cmd = CMD_ERASE;
    cmd->id ++;
    cmd->retry = 0;
    erase->sectors = f_size/0x800 + 1;                  //2k per page

        resend = 10;
Resend_E:
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
        {
            printf("send fail\n");
            *perror = -9;
            goto _exit;
        }
        memset((char*)ack_buf, 0, 128);
        retry = 100;

ReWait_E:
        if (update_read(p, (uchar*)ack_buf, 128, 200000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if (ack->id != cmd->id || ack->status != ACK_EOK)
                {
                    /* 使用网络接口时偶尔会出现读的数据是上次的情况，这里再试一次 */
                    if (resend--)
                    {
                        cmd->retry = 1;
                        goto Resend_E;
                    }
                    *perror = -10;
                    goto _exit;
                }
                *pprogress += 2;
            }
            else
            {
                printf("recv err\n");
                *perror = -11;
                goto _exit;
            }
        }
        else
        {
            if (retry--)
                goto ReWait_E;
            printf("no input\n");
            *perror = -12;
            goto _exit;
        }

    /* 烧写程序 */
    struct program_request *p_req;
    char *dst;
    p_req = (struct program_request*)cmd_buf;
    printf("file size = %d",f_size);
    float f = *pprogress;
    for(i = 0;i < f_size + 256;i = i + 256)             //每次写1k字节最后不足1k个字节的填充到1k个字节
    {
        cmd->id ++;
        cmd->cmd = CMD_PROGRAM;
        cmd->retry = 0;

        p_req->immediately = 1; /* 下位机收到数据后立即用携带的数据烧写 */
        p_req->p_addrL = (0x0000FFFF) & (0x08008000 + i);
        p_req->p_addrH = ((0x08008000 + i) & 0xFFFF0000) >> 16;
        p_req->s_addrH = 0;
        p_req->s_addrL = 0;
        p_req->size = sizeof(struct program_request);
        cmd->tsize = p_req->size + 256;                   //一次发送p_req.size字节的编程命令包头和1024s个字节的程序数据

        dst = (char*)cmd_buf + p_req->size;
        memcpy(dst,(const char*)&buf_bin[i],256);

        resend = 10;
Resend_P:
        cmd->chksum = 0;
        cmd->chksum = CheckSum((unsigned short*)cmd, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
        {
            printf("send fail\n");
            *perror = -13;
            goto _exit;
        }
        memset((char*)ack_buf, 0, 128);
        retry = 100;
ReWait_P:
         if (update_read(p, (uchar*)ack_buf, 128, 100000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if (ack->status != ACK_EOK || ack->id != cmd->id)
                {
                    printf("need resend\n");
                    if (resend)
                    {
                        resend --;
                        cmd->retry = 1;
                        goto Resend_P;
                    }
                    printf("status: %d, cid %d, aid %d\n", ack->status, cmd->id, ack->id);
                    *perror = -14;
                    goto _exit;
                }
            }
            else
            {
                if (resend)
                {
                    resend --;
                    cmd->retry = 1;
                    goto Resend_P;
                }
                printf("recv err\n");
                *perror = -15;
                goto _exit;
            }
        }
        else
        {
            if (retry--)
                goto ReWait_P;
            printf("no input\n");
            *perror = -16;
            goto _exit;
        }
        f +=(float)(25600/((float)f_size+ 256));
        *pprogress = f;
        if(*pprogress > 99)
        {
            *pprogress = 99;
        }
    }
    /* 设置入口点 */
    struct entry_request *ereq;
    ereq = (struct entry_request *)cmd_buf;
    cmd->chksum = 0;
    cmd->id ++;
    cmd->tsize = sizeof(struct entry_request);
    cmd->retry = 0;
    cmd->cmd   = CMD_ENTRY;

    ereq->run  = 1; /* 烧写后运行到此入口点 */
    ereq->entry[0] = FLASH_ENTRY_ADDR & 0x0000FFFF;
    ereq->entry[1] = (FLASH_ENTRY_ADDR & 0xFFFF0000) >> 16;
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
    {
        printf("send fail\n");
        *perror = -17;
        goto _exit;
    }

    retry = 8;
    memset((char*)ack_buf, 0, 128);

Rewait_ET:
    retry --;
    if (update_read(p, (uchar*)ack_buf, 128, 10000) == 0)
    {
        if (ack->type == PT_ACK)
        {
            if (ack->status != ACK_EOK || ack->id != cmd->id)
            {
                /* 使用网络接口时偶尔出现读到的是上次的数据，这里再读一次 */
                if (retry)
                    goto Rewait_ET;
                printf("program status: %d\n", ack->status);
                *perror = -18;
                goto _exit;
            }
        }
        else
        {
            printf("recv err\n");
            *perror = -19;
            goto _exit;
        }
    }
    else
    {
        if (retry)
        {
            goto Rewait_ET;
        }
        printf("no input\n");
        if (ereq->run == 0)
        {
            *perror = -20;
            goto _exit;
        }
    }

    *pprogress = 100;

_exit:
    free(buf_bin);
    free(cmd_buf);

    return;
}
/*
 *
 */
void UpdateHeader(MH_KNIT *p, const char *filename, char *error, char *progress)
{
    char *buf_bin;
    unsigned short *cmd_buf, *ack_buf;
    int i;

    VERSION version;
    memset(&version, 0, sizeof(VERSION));
    if(GetVersionInfo(p, &version))
    {
        printf("failed to get version\n");
    }
    if((int)(version.header[0] & 0x0F) >= 3) // 新的双系统机头板  >= 2.3.xx
    {
        return UpdateStm32Arm(p, filename, error, progress, USAGE_STM32_DSCRG);
    }

    *progress = 0;

    if((buf_bin = malloc(128*1024)) == 0)
    {
        *error = -1;
        return;
    }
    int usage = USAGE_F2407;
    char devid = 1;
    /* 解密数据 */
    if(ReadUpdateFile(filename, buf_bin, usage))
    {
        free(buf_bin);
        *error = -2;
        return;
    }
    *progress = 1;
    /* 复位2812 */
    if(p->communication == COMM_SRAM)
    {
        if(SramUpdateRack(p->pmac))
        {
            free(buf_bin);
            *error = -100;
            return;
        }
    }

    cmd_buf = (unsigned short*)malloc(1888+128);
    ack_buf = (unsigned short*)((char*)cmd_buf + 1888);
    if (!cmd_buf)
    {
        *error = -3;
        return;
    }

    struct dsp_program *program;

    program = (struct dsp_program*)buf_bin;

    /*请求DSP进入升级模式*/
    int retry, resend;
    struct cmd_hdr *cmd;
    struct ack_hdr *ack;
    struct reboot_request *reboot;//重启设备

    cmd = (struct cmd_hdr*)cmd_buf;
    ack = (struct ack_hdr*)ack_buf;

    cmd->id = 0;
    retry = 10;
Reboot:
    cmd->usrdata = 0;
    cmd->type = PT_CMD;
    cmd->retry = 0;
    cmd->cmd = CMD_REBOOT;
    cmd->tsize = sizeof(struct reboot_request);
    reboot = (struct reboot_request*)cmd;

    reboot->device = 0x16;
    reboot->reason = 0x2013;
    cmd->chksum = 0;
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    /* 清空接收区中可能存在的数据 */
    update_read(p, (uchar*)ack_buf, 128, 500000);
    update_write(p, (uchar*)cmd_buf, cmd->tsize, 1000000);

    /* 让下位机进入升级模式 */
    cmd->cmd = CMD_UPDATE;
    cmd->usrdata = FLAG_DFORWORD | devid;
    cmd->tsize = sizeof(struct cmd_hdr);//cmd_hdr为确认包包头格式

    while (retry --)
    {
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
        {
            printf("send fail and do retry...\n");
            /* 先眯会儿 */
            sleep(3);
            continue;
        }
        memset((char*)ack_buf, 0, 128);

        if (update_read(p, (uchar*)ack_buf, 128, 3000000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if ((ack->id == cmd->id) && (ack->status == ACK_EOK))
                {
                    if (ack->id > 0)
                    {
                        break;
                    }
                    cmd->id ++;
                }
            }
        }
        else
        {
            if(retry)
            {
                goto Reboot;
            }
        }
    }
    if (retry == -1)
    {
        *error = -8;
        goto _exit;
    }

    *progress = 2;

    /* 擦除扇区 */
    struct erase_request *erase;
    int sector_mask, sector_num;

    erase = (struct erase_request*)cmd;
    cmd->tsize = sizeof(struct erase_request);
    cmd->cmd = CMD_ERASE;

    sector_mask = SECTORB;
    sector_num = 2;
    for (i = 0; i < sector_num; i ++)
    {
        cmd->id ++;
        cmd->retry = 0;
        erase->sectors = sector_mask << i;

        resend = 2;
Resend_E:
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
        {
            *error = -9;
            goto _exit;
        }
        memset((char*)ack_buf, 0, 128);
        retry = 30;

ReWait_E:
        if (update_read(p, (uchar*)ack_buf, 128, 200000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if (ack->id != cmd->id || ack->status != ACK_EOK)
                {
                    /* 使用网络接口时偶尔会出现读的数据是上次的情况，这里再试一次 */
                    if (resend--)
                    {
                        cmd->retry = 1;
                        goto Resend_E;
                    }

                    *error = -10;
                    goto _exit;
                }

                *progress += 4;
            }
            else
            {
                *error = -11;
                goto _exit;
            }
        }
        else
        {
            if (retry--)
            {
                goto ReWait_E;
            }
            *error = -12;
            goto _exit;
        }
    }

    /* 烧写程序 */

    struct program_request *p_req;//编程请求包
    char *dst;
    unsigned int j;
    char block_size = 132;

    p_req = (struct program_request*)cmd_buf;

    for (i = 0; i <= program->section_cur; i ++)
    {
        /* 暂不处理入口段 */
        if (i == program->entry_sec)
        {
            continue;
        }

        for (j = 0; j < program->sec[i].size; j += block_size)
        {
            cmd->id ++;
            cmd->cmd = CMD_PROGRAM;
            cmd->retry = 0;

            p_req->immediately = 1; /* 下位机收到数据后立即用携带的数据烧写 */
            p_req->p_addrL = (0xFFFF) & (program->sec[i].start + j/2);
            p_req->p_addrH = (program->sec[i].start + j/2) >> 16;
            p_req->s_addrH = 0;
            p_req->s_addrL = 0;
            if ((program->sec[i].size - j) < block_size)
            {
                p_req->size = program->sec[i].size - j;
            }
            else
            {
                p_req->size = block_size;
            }
            cmd->tsize = sizeof(struct program_request) + p_req->size;

            dst = (char*)cmd_buf + sizeof(struct program_request);
            memcpy(dst, (const char*)&program->sec[i].data[j/2], p_req->size);

            resend = 2;
Resend_P:
            cmd->chksum = 0;
            cmd->chksum = CheckSum((unsigned short*)cmd, cmd->tsize);

            if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
            {
                *error = -13;
                goto _exit;
            }
            memset((char*)ack_buf, 0, 128);

            retry = 15;
ReWait_P:
            if (update_read(p, (uchar*)ack_buf, 128, 100000) == 0)
            {
                if (ack->type == PT_ACK)
                {
                    if (ack->status != ACK_EOK || ack->id != cmd->id)
                    {
                        if (resend)
                        {
                            resend --;
                            cmd->retry = 1;
                            goto Resend_P;
                        }
                        *error = -14;
                        goto _exit;
                    }
                }
                else
                {
                    if (resend)
                    {
                        resend --;
                        cmd->retry = 1;
                        goto Resend_P;
                    }
                    *error = -15;
                    goto _exit;
                }
            }
            else
            {
                if (retry--)
                {
                    goto ReWait_P;
                }
                *error = -16;
                goto _exit;
            }

            *progress += (90.0/(program->ptsize/block_size));
        }
    }

    /* 设置入口点 */
    struct entry_request *ereq;
    ereq = (struct entry_request *)cmd_buf;
    cmd->chksum = 0;
    cmd->id ++;
    cmd->tsize = sizeof(struct entry_request);
    cmd->retry = 0;
    cmd->cmd   = CMD_ENTRY;

    ereq->run  = 1; /* 烧写后运行到此入口点 */
    ereq->entry[0] = program->sec[program->entry_sec].data[0];
    ereq->entry[1] = program->sec[program->entry_sec].data[1];
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
    {
        *error = -17;
        goto _exit;
    }

    retry = 8;
    memset((char*)ack_buf, 0, 128);

Rewait_ET:
    retry --;
    if (update_read(p, (uchar*)ack_buf, 128, 10000) == 0)
    {
        if (ack->type == PT_ACK)
        {
            if (ack->status != ACK_EOK || ack->id != cmd->id)
            {
                /* 使用网络接口时偶尔出现读到的是上次的数据，这里再读一次 */
                if (retry)
                {
                    goto Rewait_ET;
                }
                *error = -18;
                goto _exit;
            }
        }
        else
        {
            *error = -19;
            goto _exit;
        }
    }
    else
    {
        if (retry)
        {
            goto Rewait_ET;
        }
        if (ereq->run == 0)
        {
            *error = -20;
            goto _exit;
        }
    }

    *progress = 100;

_exit:

    free(buf_bin);
    free(cmd_buf);
}
/*
 *
 */
void UpdateBoard(MH_KNIT *p, const char *filename, char *error, char *progress)
{
    char *buf_bin;
    unsigned short *cmd_buf, *ack_buf;
    int i;

    *progress = 0;

    if ((buf_bin = malloc(128*1024)) == 0)
    {
        *error = -1;
        return;
    }

    int usage = USAGE_F2802;
    /* 解密数据 */
    if(ReadUpdateFile(filename, buf_bin, usage))
    {
        free(buf_bin);
        *error = -2;
        return;
    }

    *progress = 1;
    /* 复位2812 */
    if(p->communication == COMM_SRAM)
    {
        if(SramUpdateRack(p->pmac))
        {
            free(buf_bin);
            *error = -100;
            return;
        }
    }

    cmd_buf = (unsigned short*)malloc(1888+128);
    ack_buf = (unsigned short*)((char*)cmd_buf + 1888);
    if (!cmd_buf)
    {
        *error = -3;
        return;
    }

    /**** 合法性检查 ****/
    struct dsp_program *program;

    program = (struct dsp_program*)buf_bin;
    /* 至少需要一个程序段和一个入口段 */
    if (program->section_cur < 1)
    {
        *error = -4;
        goto _exit;
    }

    for (i = 0; i <= program->section_cur; i ++)
    {
        /* 检查程序的加载空间是否占用bootloader的加载空间 */
        if (program->sec[i].start < 0x3F2000 ||
                ((program->sec[i].start >= 0x3F6000) && (program->sec[i].start < 0x3F7F80)))
        {
            *error = -5;
            goto _exit;
        }
    }
    if (program->entry_sec == -1)
    {
        *error = -6;
        goto _exit;
    }
    else
    {
        /* 除去bootloader区F2802可用空间为2*0x2000*2字节 */
        if (program->ptsize > 2*0x2000*2+4)
        {
            *error = -7;
            goto _exit;
        }
    }

    /******************************************************/
    int retry, resend;
    struct cmd_hdr *cmd;
    struct ack_hdr *ack;
    struct reboot_request *reboot;

    cmd = (struct cmd_hdr*)cmd_buf;
    ack = (struct ack_hdr*)ack_buf;

    retry = 8;

    cmd->id = 0;
Reboot:
    cmd->usrdata = 0;
    cmd->type = PT_CMD;
    cmd->retry = 0;
    /* 首先让2802从应用中复位进入Boot模式 */
    cmd->cmd = CMD_REBOOT;
    cmd->tsize = sizeof(struct reboot_request);
    reboot = (struct reboot_request*)cmd;

    reboot->device = 0x100;  //2802在应用中的CAN通信ID
    reboot->reason = 0x2013;
    cmd->chksum = 0;
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    /* 清空接收区中可能存在的数据 */
    update_read(p, (uchar*)ack_buf, 128, 500000);
    update_write(p, (uchar*)cmd_buf, cmd->tsize, 800000);

    /* 查询下位机是否准备好升级 */
    cmd->cmd = CMD_UPDATE;
    /* 数据转发给ID为3的dsp（进入Bootloader后重新分配了各DSP的ID） */
    cmd->usrdata = FLAG_DFORWORD | 3;
    cmd->tsize = sizeof(struct cmd_hdr);

    while (retry --)
    {
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 20000) != 0)
        {
            /* 先眯会儿 */
            sleep(2);
            continue;
        }
        memset((char*)ack_buf, 0, 128);

        if (update_read(p, (uchar*)ack_buf, 128, 3000000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if ((ack->id == cmd->id) && (ack->status == ACK_EOK))
                {
                    if (ack->id > 0)
                    {
                        break;
                    }
                    cmd->id ++;
                }
            }
        }
        else
        {
            goto Reboot;
        }
    }
    if (retry == -1)
    {
        *error = -8;
        goto _exit;
    }

    *progress = 2;

    /* 擦除扇区 */
    struct erase_request *erase;
    erase = (struct erase_request*)cmd;
    cmd->tsize = sizeof(struct erase_request);
    cmd->cmd = CMD_ERASE;

    for (i = 0; i < 2; i ++)
    {
        cmd->id ++;
        cmd->retry = 0;
        erase->sectors = SECTORB << i;

        resend = 2;
Resend_E:
        cmd->chksum = 0;
        cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

        if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 100000) != 0)
        {
            *error = -9;
            goto _exit;
        }
        memset((char*)ack_buf, 0, 128);

        retry = 20;
ReWait_E:
        if (update_read(p, (uchar*)ack_buf, 128, 300000) == 0)
        {
            if (ack->type == PT_ACK)
            {
                if (ack->id != cmd->id || ack->status != ACK_EOK)
                {
                    if (resend--)
                    {
                        cmd->retry = 1;
                        goto Resend_E;
                    }
                    *error = -10;
                    goto _exit;
                }

                *progress += 4;
            }
            else
            {
                *error = -11;
                goto _exit;
            }
        }
        else
        {
            if (retry--)
            {
                goto ReWait_E;
            }
            *error = -12;
            goto _exit;
        }
    }

    /* 烧写程序 */

    struct program_request *p_req;
    char *dst;
    unsigned int j;
    unsigned block_size = 132;

    p_req = (struct program_request*)cmd_buf;

    for (i = 0; i <= program->section_cur; i ++)
    {
        /* 暂不处理入口段 */
        if (i == program->entry_sec)
        {
            continue;
        }

        for (j = 0; j < program->sec[i].size; j += block_size)
        {
            cmd->id ++;
            cmd->cmd = CMD_PROGRAM;
            cmd->retry = 0;

            p_req->immediately = 1; /* 下位机收到数据后立即用携带的数据烧写 */
            p_req->p_addrL = (0xFFFF) & (program->sec[i].start + j/2);
            p_req->p_addrH = (program->sec[i].start + j/2) >> 16;
            p_req->s_addrH = 0;
            p_req->s_addrL = 0;
            if ((program->sec[i].size - j) < block_size)
            {
                p_req->size = program->sec[i].size - j;
            }
            else
            {
                p_req->size = block_size;
            }
            cmd->tsize = sizeof(struct program_request) + p_req->size;

            dst = (char*)cmd_buf + sizeof(struct program_request);
            memcpy(dst, (const char*)&program->sec[i].data[j/2], p_req->size);

            resend = 2;
Resend_P:
            retry = 15;
            cmd->chksum = 0;
            cmd->chksum = CheckSum((unsigned short*)cmd, cmd->tsize);

            if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 10000) != 0)
            {
                *error = -13;
                goto _exit;
            }
            memset((char*)ack_buf, 0, 128);

Rewait_P:
            if (update_read(p, (uchar*)ack_buf, 128, 100000) == 0)
            {
                if (ack->type == PT_ACK)
                {
                    if (ack->status != ACK_EOK || ack->id != cmd->id)
                    {
                        if (resend)
                        {
                            cmd->retry = 1;
                            resend --;
                            goto Resend_P;
                        }
                        *error = -14;
                        goto _exit;
                    }
                }
                else
                {
                    if (resend)
                    {
                        cmd->retry = 1;
                        resend --;
                        goto Resend_P;
                    }
                    *error = -15;
                    goto _exit;
                }
            }
            else
            {
                if (retry)
                {
                    retry --;
                    goto Rewait_P;
                }
                *error = -16;
                goto _exit;
            }

            *progress += (90.0/(program->ptsize/block_size));
        }
    }

    /* 设置入口点 */
    struct entry_request *ereq;
    ereq = (struct entry_request *)cmd_buf;
    cmd->chksum = 0;
    cmd->id ++;
    cmd->tsize = sizeof(struct entry_request);
    cmd->retry = 0;
    cmd->cmd   = CMD_ENTRY;

    ereq->run  = 1; /* 烧写后运行到此入口点 */
    ereq->entry[0] = program->sec[program->entry_sec].data[0];
    ereq->entry[1] = program->sec[program->entry_sec].data[1];
    cmd->chksum = CheckSum(cmd_buf, cmd->tsize);

    if (update_write(p, (uchar*)cmd_buf, cmd->tsize, 0) != 0)
    {
        *error = -17;
        goto _exit;
    }

    retry = 8;
    memset((char*)ack_buf, 0, 128);

Rewait_ET:
    if (update_read(p, (uchar*)ack_buf, 128, 20000) == 0)
    {
        if (ack->type == PT_ACK)
        {
            if (ack->status != ACK_EOK || ack->id != cmd->id)
            {
                if (retry)
                {
                    retry --;
                    goto Rewait_ET;
                }
                *error = -18;
                goto _exit;
            }
        }
        else
        {
            *error = -19;
            goto _exit;
        }
    }
    else
    {
        if (retry)
        {
            retry --;
            goto Rewait_ET;
        }
        if (ereq->run == 0)
        {
            *error = -20;
            goto _exit;
        }
    }

    *progress = 100;

_exit:

    free(buf_bin);
    free(cmd_buf);
}
/*
 *
 */
void UpdateStitch(MH_KNIT *p, const char *filename, char *error, char *progress)
{
    *error = 0;
    *progress = 0;
    while(*progress < 100)
    {
        *progress += 1;
        usleep(20000);
    }
}
/*
 *
 */
void UpdateGauze(MH_KNIT *p, const char *filename, char *error, char *progress)
{
    UpdateStm32Arm(p, filename, error, progress, USAGE_STM32_GAUZE);
}
